home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / phile.c < prev    next >
C/C++ Source or Header  |  1996-05-15  |  30KB  |  1,062 lines

  1. /*
  2.  * Program:    File routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    25 August 1993
  13.  * Last Edited:    15 May 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. #include <stdio.h>
  37. #include <ctype.h>
  38. #include <errno.h>
  39. extern int errno;        /* just in case */
  40. #include <signal.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <pwd.h>
  44. #include <sys/stat.h>
  45. #include <sys/time.h>
  46. #include "phile.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "dummy.h"
  50.  
  51. /* File routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER philedriver = {
  57.   "phile",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   phile_valid,            /* mailbox is valid for us */
  60.   phile_parameters,        /* manipulate parameters */
  61.   phile_find,            /* find mailboxes */
  62.   phile_find_bboards,        /* find bboards */
  63.   phile_find_all,        /* find all mailboxes */
  64.   phile_find_all_bboards,    /* find all bboards */
  65.   phile_subscribe,        /* subscribe to mailbox */
  66.   phile_unsubscribe,        /* unsubscribe from mailbox */
  67.   phile_subscribe_bboard,    /* subscribe to bboard */
  68.   phile_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   phile_create,            /* create mailbox */
  70.   phile_delete,            /* delete mailbox */
  71.   phile_rename,            /* rename mailbox */
  72.   phile_open,            /* open mailbox */
  73.   phile_close,            /* close mailbox */
  74.   phile_fetchfast,        /* fetch message "fast" attributes */
  75.   phile_fetchflags,        /* fetch message flags */
  76.   phile_fetchstructure,        /* fetch message envelopes */
  77.   phile_fetchheader,        /* fetch message header only */
  78.   phile_fetchtext,        /* fetch message body only */
  79.   phile_fetchbody,        /* fetch message body section */
  80.   phile_setflag,        /* set message flag */
  81.   phile_clearflag,        /* clear message flag */
  82.   phile_search,            /* search for message based on criteria */
  83.   phile_ping,            /* ping mailbox to see if still alive */
  84.   phile_check,            /* check for new messages */
  85.   phile_expunge,        /* expunge deleted messages */
  86.   phile_copy,            /* copy messages to another mailbox */
  87.   phile_move,            /* move messages to another mailbox */
  88.   phile_append,            /* append string message to mailbox */
  89.   phile_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM phileproto = {&philedriver};
  94.  
  95. /* File validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *phile_valid (char *name)
  101. {
  102.   char tmp[MAILTMPLEN];
  103.   return phile_isvalid (name,tmp) ? &philedriver : NIL;
  104. }
  105.  
  106.  
  107. /* File test for valid mailbox
  108.  * Accepts: mailbox name
  109.  * Returns: T if valid, NIL otherwise
  110.  */
  111.  
  112. int phile_isvalid (char *name,char *tmp)
  113. {
  114.   struct stat sbuf;
  115.   char *s;
  116.                 /* INBOX never accepted, any other name is */
  117.   return ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  118.       (s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) &&
  119.       !(sbuf.st_mode & S_IFDIR) && sbuf.st_size);
  120. }
  121.  
  122. /* File manipulate driver parameters
  123.  * Accepts: function code
  124.  *        function-dependent value
  125.  * Returns: function-dependent return value
  126.  */
  127.  
  128. void *phile_parameters (long function,void *value)
  129. {
  130.   return NIL;
  131. }
  132.  
  133. /* File find list of mailboxes
  134.  * Accepts: mail stream
  135.  *        pattern to search
  136.  */
  137.  
  138. void phile_find (MAILSTREAM *stream,char *pat)
  139. {
  140.   if (stream) dummy_find (NIL,pat);
  141. }
  142.  
  143.  
  144. /* File find list of bboards
  145.  * Accepts: mail stream
  146.  *        pattern to search
  147.  */
  148.  
  149. void phile_find_bboards (MAILSTREAM *stream,char *pat)
  150. {
  151.   if (stream) dummy_find_bboards (NIL,pat);
  152. }
  153.  
  154.  
  155. /* File find list of all mailboxes
  156.  * Accepts: mail stream
  157.  *        pattern to search
  158.  */
  159.  
  160. void phile_find_all (MAILSTREAM *stream,char *pat)
  161. {
  162.   if (stream) dummy_find_all (NIL,pat);
  163. }
  164.  
  165.  
  166. /* File find list of all bboards
  167.  * Accepts: mail stream
  168.  *        pattern to search
  169.  */
  170.  
  171. void phile_find_all_bboards (MAILSTREAM *stream,char *pat)
  172. {
  173.   if (stream) dummy_find_all_bboards (NIL,pat);
  174. }
  175.  
  176. /* File subscribe to mailbox
  177.  * Accepts: mail stream
  178.  *        mailbox to add to subscription list
  179.  * Returns: T on success, NIL on failure
  180.  */
  181.  
  182. long phile_subscribe (MAILSTREAM *stream,char *mailbox)
  183. {
  184.   char tmp[MAILTMPLEN];
  185.   return sm_subscribe (mailboxfile (tmp,mailbox));
  186. }
  187.  
  188.  
  189. /* File unsubscribe to mailbox
  190.  * Accepts: mail stream
  191.  *        mailbox to delete from subscription list
  192.  * Returns: T on success, NIL on failure
  193.  */
  194.  
  195. long phile_unsubscribe (MAILSTREAM *stream,char *mailbox)
  196. {
  197.   char tmp[MAILTMPLEN];
  198.   return sm_unsubscribe (mailboxfile (tmp,mailbox));
  199. }
  200.  
  201.  
  202. /* File subscribe to bboard
  203.  * Accepts: mail stream
  204.  *        bboard to add to subscription list
  205.  * Returns: T on success, NIL on failure
  206.  */
  207.  
  208. long phile_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  209. {
  210.   char tmp[MAILTMPLEN];
  211.   sprintf (tmp,"*%s",mailbox);
  212.   return sm_subscribe (tmp);
  213. }
  214.  
  215.  
  216. /* File unsubscribe to bboard
  217.  * Accepts: mail stream
  218.  *        bboard to delete from subscription list
  219.  * Returns: T on success, NIL on failure
  220.  */
  221.  
  222. long phile_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  223. {
  224.   char tmp[MAILTMPLEN];
  225.   sprintf (tmp,"*%s",mailbox);
  226.   return sm_subscribe (tmp);
  227. }
  228.  
  229. /* File create mailbox
  230.  * Accepts: MAIL stream
  231.  *        mailbox name to create
  232.  * Returns: T on success, NIL on failure
  233.  */
  234.  
  235. long phile_create (MAILSTREAM *stream,char *mailbox)
  236. {
  237.   return dummy_create (stream,mailbox);
  238. }
  239.  
  240.  
  241. /* File delete mailbox
  242.  * Accepts: MAIL stream
  243.  *        mailbox name to delete
  244.  * Returns: T on success, NIL on failure
  245.  */
  246.  
  247. long phile_delete (MAILSTREAM *stream,char *mailbox)
  248. {
  249.   return dummy_delete (stream,mailbox);
  250. }
  251.  
  252.  
  253. /* File rename mailbox
  254.  * Accepts: MAIL stream
  255.  *        old mailbox name
  256.  *        new mailbox name (or NIL for delete)
  257.  * Returns: T on success, NIL on failure
  258.  */
  259.  
  260. long phile_rename (MAILSTREAM *stream,char *old,char *new)
  261. {
  262.   return dummy_rename (stream,old,new);
  263. }
  264.  
  265. /* File open
  266.  * Accepts: Stream to open
  267.  * Returns: Stream on success, NIL on failure
  268.  */
  269.  
  270. MAILSTREAM *phile_open (MAILSTREAM *stream)
  271. {
  272.   int i,k,fd;
  273.   unsigned long j,m;
  274.   char *s,tmp[MAILTMPLEN];
  275.   struct passwd *pw;
  276.   struct stat sbuf;
  277.   struct tm *t;
  278.   MESSAGECACHE *elt;
  279.                 /* return prototype for OP_PROTOTYPE call */
  280.   if (!stream) return &phileproto;
  281.   if (LOCAL) {            /* close old file if stream being recycled */
  282.     phile_close (stream);    /* dump and save the changes */
  283.     stream->dtb = &philedriver;    /* reattach this driver */
  284.     mail_free_cache (stream);    /* clean up cache */
  285.   }    
  286.                 /* canonicalize the stream mailbox name */
  287.   mailboxfile (tmp,stream->mailbox);
  288.   if (*stream->mailbox != '*') {/* canonicalize name */
  289.     fs_give ((void **) &stream->mailbox);
  290.     stream->mailbox = cpystr (tmp);
  291.   }
  292.                 /* open mailbox */
  293.   if (stat (tmp,&sbuf) || (fd = open (tmp,O_RDONLY,NIL)) < 0) {
  294.     sprintf (tmp,"Unable to open file %s",stream->mailbox);
  295.     mm_log (tmp,ERROR);
  296.     return NIL;
  297.   }
  298.   stream->local = fs_get (sizeof (PHILELOCAL));
  299.                 /* initialize file information */
  300.   (elt = mail_elt (stream,1))->rfc822_size = sbuf.st_size;
  301.   elt->valid = elt->recent = T;    /* mark valid flags */
  302.   stream->sequence++;        /* bump sequence number */
  303.                 /* only one message */
  304.   stream->nmsgs = stream->recent = 1;
  305.   stream->rdonly = T;        /* make sure upper level knows readonly */
  306.                 /* instantiate a new envelope and body */
  307.   LOCAL->env = mail_newenvelope ();
  308.   LOCAL->body = mail_newbody ();
  309.  
  310.   t = gmtime (&sbuf.st_mtime);    /* get UTC time and Julian day */
  311.   i = t->tm_hour * 60 + t->tm_min;
  312.   k = t->tm_yday;
  313.   t = localtime(&sbuf.st_mtime);/* get local time */
  314.                 /* calulate time delta */
  315.   i = t->tm_hour * 60 + t->tm_min - i;
  316.   if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60;
  317.   k = abs (i);            /* time from UTC either way */
  318.   elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec;
  319.   elt->day = t->tm_mday; elt->month = t->tm_mon + 1;
  320.   elt->year = t->tm_year - (BASEYEAR - 1900);
  321.   elt->zoccident = (k == i) ? 0 : 1;
  322.   elt->zhours = k/60;
  323.   elt->zminutes = k % 60;
  324.   sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
  325.        days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900,
  326.        t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+',
  327.        elt->zhours,elt->zminutes);
  328.                 /* set up Date field */
  329.   LOCAL->env->date = cpystr (tmp);
  330.  
  331.                 /* fill in From field from file owner */
  332.   LOCAL->env->from = mail_newaddr ();
  333.   if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name);
  334.   else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid);
  335.   LOCAL->env->from->mailbox = cpystr (tmp);
  336.   LOCAL->env->from->host = cpystr (mylocalhost ());
  337.                 /* set subject to be mailbox name */
  338.   LOCAL->env->subject = cpystr (stream->mailbox);
  339.                 /* slurp the data */
  340.   read (fd,LOCAL->buf = (char *) fs_get (elt->rfc822_size+1),elt->rfc822_size);
  341.   LOCAL->buf[elt->rfc822_size] = '\0';
  342.   close (fd);            /* close the file */
  343.                 /* analyze data type */
  344.   if (i = phile_type ((unsigned char *) LOCAL->buf,elt->rfc822_size,&j)) {
  345.     LOCAL->body->type = TYPETEXT;
  346.     LOCAL->body->subtype = cpystr ("PLAIN");
  347.     if (!(i & PTYPECRTEXT)) {    /* change Internet newline format as needed */
  348.       STRING bs;
  349.       INIT (&bs,mail_string,(void *) (s = LOCAL->buf),elt->rfc822_size);
  350.       LOCAL->buf = (char *) fs_get ((m = strcrlflen (&bs) + 1));
  351.       strcrlfcpy (&LOCAL->buf,&m,s,elt->rfc822_size);
  352.       elt->rfc822_size = m;    /* update size */
  353.       fs_give ((void **) &s);    /* flush original UNIX-format string */
  354.     }
  355.     LOCAL->body->parameter = mail_newbody_parameter ();
  356.     LOCAL->body->parameter->attribute = cpystr ("charset");
  357.     LOCAL->body->parameter->value =
  358.       cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" :
  359.           (i & PTYPEISO2022KR) ? "ISO-2022-KR" :
  360.           (i & PTYPE8) ? "ISO-8859-1" : "US-ASCII");
  361.     LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT;
  362.     LOCAL->body->size.lines = j;
  363.   }
  364.   else {            /* binary data */
  365.     LOCAL->body->type = TYPEAPPLICATION;
  366.     LOCAL->body->subtype = cpystr ("OCTET-STREAM");
  367.     LOCAL->body->parameter = mail_newbody_parameter ();
  368.     LOCAL->body->parameter->attribute = cpystr ("name");
  369.     LOCAL->body->parameter->value =
  370.       cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox);
  371.     LOCAL->body->encoding = ENCBASE64;
  372.     LOCAL->buf = (char *) rfc822_binary (s = LOCAL->buf,elt->rfc822_size,
  373.                      &elt->rfc822_size);
  374.     fs_give ((void **) &s);    /* flush originary binary contents */
  375.   }
  376.   LOCAL->body->size.bytes = elt->rfc822_size;
  377.   elt->rfc822_size += strlen (phile_fetchheader (stream,1));
  378.   mail_exists (stream,1);    /* make sure upper level knows */
  379.   mail_recent (stream,1);
  380.   return stream;        /* return stream alive to caller */
  381. }
  382.  
  383. /* File determine data type
  384.  * Accepts: data to examine
  385.  *        size of data
  386.  *        pointer to line count return
  387.  * Returns: PTYPE mask of data type
  388.  */
  389.  
  390. int phile_type (unsigned char *s,unsigned long i,unsigned long *j)
  391. {
  392.   int ret = PTYPETEXT;
  393.   char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  394.   *j = 0;            /* no lines */
  395.                 /* check type of every character */
  396.   while (i--) switch (charvec[*s++]) {
  397.   case 'A':
  398.     ret |= PTYPE8;        /* 8bit character */
  399.     break;
  400.   case 'a':
  401.     break;            /* ASCII character */
  402.   case 'b':
  403.     return PTYPEBINARY;        /* binary byte seen, stop immediately */
  404.   case 'c':
  405.     ret |= PTYPECRTEXT;        /* CR indicates Internet text */
  406.     break;
  407.   case 'e':            /* ESC */
  408.     if (*s == '$') {        /* ISO-2022 sequence? */
  409.       if (s[1] == 'B' || s[1] == '@') ret |= PTYPEISO2022JP;
  410.       else if (s[1] == ')' && s[2] == 'C') ret |= PTYPEISO2022JP;
  411.     }
  412.     break;
  413.   case 'l':            /* newline */
  414.     (*j)++;
  415.     break;
  416.   }
  417.   return ret;            /* return type of data */
  418. }
  419.  
  420. /* File close
  421.  * Accepts: MAIL stream
  422.  */
  423.  
  424. void phile_close (MAILSTREAM *stream)
  425. {
  426.   if (LOCAL) {            /* only if a file is open */
  427.                 /* free local texts */
  428.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  429.                 /* nuke the local data */
  430.     fs_give ((void **) &stream->local);
  431.     stream->dtb = NIL;        /* log out the DTB */
  432.   }
  433. }
  434.  
  435.  
  436. /* File fetch fast information
  437.  * Accepts: MAIL stream
  438.  *        sequence
  439.  */
  440.  
  441. void phile_fetchfast (MAILSTREAM *stream,char *sequence)
  442. {
  443.   return;            /* no-op for local mail */
  444. }
  445.  
  446.  
  447. /* File fetch flags
  448.  * Accepts: MAIL stream
  449.  *        sequence
  450.  */
  451.  
  452. void phile_fetchflags (MAILSTREAM *stream,char *sequence)
  453. {
  454.   return;            /* no-op for local mail */
  455. }
  456.  
  457. /* File fetch structure
  458.  * Accepts: MAIL stream
  459.  *        message # to fetch
  460.  *        pointer to return body
  461.  * Returns: envelope of this message, body returned in body value
  462.  *
  463.  * Fetches the "fast" information as well
  464.  */
  465.  
  466. ENVELOPE *phile_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  467. {
  468.   if (body) *body = LOCAL->body;
  469.   return LOCAL->env;        /* return the envelope */
  470. }
  471.  
  472.  
  473. /* File fetch message header
  474.  * Accepts: MAIL stream
  475.  *        message # to fetch
  476.  * Returns: message header in RFC822 format
  477.  */
  478.  
  479. char *phile_fetchheader (MAILSTREAM *stream,long msgno)
  480. {
  481.   BODY *body;
  482.   ENVELOPE *env = phile_fetchstructure (stream,msgno,&body);
  483.   rfc822_header (LOCAL->tmp,env,body);
  484.   return LOCAL->tmp;
  485. }
  486.  
  487.  
  488. /* File fetch message text (body only)
  489.  * Accepts: MAIL stream
  490.  *        message # to fetch
  491.  * Returns: message text in RFC822 format
  492.  */
  493.  
  494. char *phile_fetchtext (MAILSTREAM *stream,long msgno)
  495. {
  496.                 /* mark message as seen */
  497.   mail_elt (stream,msgno)->seen = T;
  498.   return LOCAL->buf;
  499. }
  500.  
  501.  
  502. /* File fetch message body as a structure
  503.  * Accepts: Mail stream
  504.  *        message # to fetch
  505.  *        section specifier
  506.  *        pointer to length
  507.  * Returns: pointer to section of message body
  508.  */
  509.  
  510. char *phile_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  511. {
  512.   MESSAGECACHE *elt = mail_elt (stream,m);
  513.   if (!strcmp (s,"0")) {    /* BODY[0] case? */
  514.     char *s = phile_fetchheader (stream,1);
  515.     *len = strlen (s);
  516.     return s;
  517.   }
  518.   else if (strcmp (s,"1")) return NIL;
  519.   *len = LOCAL->body->size.bytes;
  520.   elt->seen = T;        /* mark message as seen */
  521.   return LOCAL->buf;
  522. }
  523.  
  524. /* File set flag
  525.  * Accepts: MAIL stream
  526.  *        sequence
  527.  *        flag(s)
  528.  */
  529.  
  530. void phile_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  531. {
  532.   MESSAGECACHE *elt;
  533.   long i;
  534.   short f = phile_getflags (stream,flag);
  535.   if (!f) return;        /* no-op if no flags to modify */
  536.                 /* get sequence and loop on it */
  537.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  538.     if ((elt = mail_elt (stream,i))->sequence) {
  539.                 /* set all requested flags */
  540.       if (f&fSEEN) elt->seen = T;
  541.       if (f&fDELETED) elt->deleted = T;
  542.       if (f&fFLAGGED) elt->flagged = T;
  543.       if (f&fANSWERED) elt->answered = T;
  544.     }
  545. }
  546.  
  547.  
  548. /* File clear flag
  549.  * Accepts: MAIL stream
  550.  *        sequence
  551.  *        flag(s)
  552.  */
  553.  
  554. void phile_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  555. {
  556.   MESSAGECACHE *elt;
  557.   long i;
  558.   short f = phile_getflags (stream,flag);
  559.   if (!f) return;        /* no-op if no flags to modify */
  560.                 /* get sequence and loop on it */
  561.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  562.     if ((elt = mail_elt (stream,i))->sequence) {
  563.                 /* clear all requested flags */
  564.       if (f&fSEEN) elt->seen = NIL;
  565.       if (f&fDELETED) elt->deleted = NIL;
  566.       if (f&fFLAGGED) elt->flagged = NIL;
  567.       if (f&fANSWERED) elt->answered = NIL;
  568.     }
  569. }
  570.  
  571. /* File search for messages
  572.  * Accepts: MAIL stream
  573.  *        search criteria
  574.  */
  575.  
  576. void phile_search (MAILSTREAM *stream,char *criteria)
  577. {
  578.   long i,n;
  579.   char *d,tmp[MAILTMPLEN];
  580.   search_t f;
  581.                 /* initially all searched */
  582.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  583.                 /* get first criterion */
  584.   if (criteria && (criteria = strtok (criteria," "))) {
  585.                 /* for each criterion */
  586.     for (; criteria; (criteria = strtok (NIL," "))) {
  587.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  588.       switch (*ucase (criteria)) {
  589.       case 'A':            /* possible ALL, ANSWERED */
  590.     if (!strcmp (criteria+1,"LL")) f = phile_search_all;
  591.     else if (!strcmp (criteria+1,"NSWERED")) f = phile_search_answered;
  592.     break;
  593.       case 'B':            /* possible BCC, BEFORE, BODY */
  594.     if (!strcmp (criteria+1,"CC"))
  595.       f = phile_search_string (phile_search_bcc,&d,&n);
  596.     else if (!strcmp (criteria+1,"EFORE"))
  597.       f = phile_search_date (phile_search_before,&n);
  598.     else if (!strcmp (criteria+1,"ODY"))
  599.       f = phile_search_string (phile_search_body,&d,&n);
  600.     break;
  601.       case 'C':            /* possible CC */
  602.     if (!strcmp (criteria+1,"C")) 
  603.       f = phile_search_string (phile_search_cc,&d,&n);
  604.     break;
  605.       case 'D':            /* possible DELETED */
  606.     if (!strcmp (criteria+1,"ELETED")) f = phile_search_deleted;
  607.     break;
  608.       case 'F':            /* possible FLAGGED, FROM */
  609.     if (!strcmp (criteria+1,"LAGGED")) f = phile_search_flagged;
  610.     else if (!strcmp (criteria+1,"ROM"))
  611.       f = phile_search_string (phile_search_from,&d,&n);
  612.     break;
  613.       case 'K':            /* possible KEYWORD */
  614.     if (!strcmp (criteria+1,"EYWORD"))
  615.       f = phile_search_flag (phile_search_keyword,&d);
  616.     break;
  617.       case 'N':            /* possible NEW */
  618.     if (!strcmp (criteria+1,"EW")) f = phile_search_new;
  619.     break;
  620.  
  621.       case 'O':            /* possible OLD, ON */
  622.     if (!strcmp (criteria+1,"LD")) f = phile_search_old;
  623.     else if (!strcmp (criteria+1,"N"))
  624.       f = phile_search_date (phile_search_on,&n);
  625.     break;
  626.       case 'R':            /* possible RECENT */
  627.     if (!strcmp (criteria+1,"ECENT")) f = phile_search_recent;
  628.     break;
  629.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  630.     if (!strcmp (criteria+1,"EEN")) f = phile_search_seen;
  631.     else if (!strcmp (criteria+1,"INCE"))
  632.       f = phile_search_date (phile_search_since,&n);
  633.     else if (!strcmp (criteria+1,"UBJECT"))
  634.       f = phile_search_string (phile_search_subject,&d,&n);
  635.     break;
  636.       case 'T':            /* possible TEXT, TO */
  637.     if (!strcmp (criteria+1,"EXT"))
  638.       f = phile_search_string (phile_search_text,&d,&n);
  639.     else if (!strcmp (criteria+1,"O"))
  640.       f = phile_search_string (phile_search_to,&d,&n);
  641.     break;
  642.       case 'U':            /* possible UN* */
  643.     if (criteria[1] == 'N') {
  644.       if (!strcmp (criteria+2,"ANSWERED")) f = phile_search_unanswered;
  645.       else if (!strcmp (criteria+2,"DELETED")) f = phile_search_undeleted;
  646.       else if (!strcmp (criteria+2,"FLAGGED")) f = phile_search_unflagged;
  647.       else if (!strcmp (criteria+2,"KEYWORD"))
  648.         f = phile_search_flag (phile_search_unkeyword,&d);
  649.       else if (!strcmp (criteria+2,"SEEN")) f = phile_search_unseen;
  650.     }
  651.     break;
  652.       default:            /* we will barf below */
  653.     break;
  654.       }
  655.       if (!f) {            /* if can't determine any criteria */
  656.     sprintf (tmp,"Unknown search criterion: %.30s",criteria);
  657.     mm_log (tmp,ERROR);
  658.     return;
  659.       }
  660.                 /* run the search criterion */
  661.       for (i = 1; i <= stream->nmsgs; ++i)
  662.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  663.       mail_elt (stream,i)->searched = NIL;
  664.     }
  665.                 /* report search results to main program */
  666.     for (i = 1; i <= stream->nmsgs; ++i)
  667.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  668.   }
  669. }
  670.  
  671. /* File ping mailbox
  672.  * Accepts: MAIL stream
  673.  * Returns: T if stream alive, else NIL
  674.  * No-op for readonly files, since read/writer can expunge it from under us!
  675.  */
  676.  
  677. long phile_ping (MAILSTREAM *stream)
  678. {
  679.   return T;
  680. }
  681.  
  682. /* File check mailbox
  683.  * Accepts: MAIL stream
  684.  * No-op for readonly files, since read/writer can expunge it from under us!
  685.  */
  686.  
  687. void phile_check (MAILSTREAM *stream)
  688. {
  689.   mm_log ("Check completed",NIL);
  690. }
  691.  
  692. /* File expunge mailbox
  693.  * Accepts: MAIL stream
  694.  */
  695.  
  696. void phile_expunge (MAILSTREAM *stream)
  697. {
  698.   mm_log ("Expunge ignored on readonly mailbox",NIL);
  699. }
  700.  
  701. /* File copy message(s)
  702.  * Accepts: MAIL stream
  703.  *        sequence
  704.  *        destination mailbox
  705.  * Returns: T if copy successful, else NIL
  706.  */
  707.  
  708. long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  709. {
  710.   mm_log ("Copy not valid for file",ERROR);
  711.   return NIL;
  712. }
  713.  
  714.  
  715. /* File move message(s)
  716.  * Accepts: MAIL stream
  717.  *        sequence
  718.  *        destination mailbox
  719.  * Returns: T if move successful, else NIL
  720.  */
  721.  
  722. long phile_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  723. {
  724.   mm_log ("Move not valid for file",ERROR);
  725.   return NIL;
  726. }
  727.  
  728.  
  729. /* File append message from stringstruct
  730.  * Accepts: MAIL stream
  731.  *        destination mailbox
  732.  *        stringstruct of messages to append
  733.  * Returns: T if append successful, else NIL
  734.  */
  735.  
  736. long phile_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  737.            STRING *message)
  738. {
  739.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  740.   sprintf (tmp,"Can't append - file \"%s\" is not in valid mailbox format",
  741.        mailboxfile (file,mailbox));
  742.   mm_log (tmp,ERROR);
  743.   return NIL;
  744. }
  745.  
  746.  
  747. /* File garbage collect stream
  748.  * Accepts: Mail stream
  749.  *        garbage collection flags
  750.  */
  751.  
  752. void phile_gc (MAILSTREAM *stream,long gcflags)
  753. {
  754.   /* nothing here for now */
  755. }
  756.  
  757. /* Internal routines */
  758.  
  759.  
  760. /* Parse flag list
  761.  * Accepts: MAIL stream
  762.  *        flag list as a character string
  763.  * Returns: flag command list
  764.  */
  765.  
  766.  
  767. short phile_getflags (MAILSTREAM *stream,char *flag)
  768. {
  769.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  770.   short f = 0;
  771.   short i,j;
  772.   if (flag && *flag) {        /* no-op if no flag string */
  773.                 /* check if a list and make sure valid */
  774.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  775.       mm_log ("Bad flag list",ERROR);
  776.       return NIL;
  777.     }
  778.                 /* copy the flag string w/o list construct */
  779.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  780.     tmp[j] = '\0';
  781.     t = ucase (tmp);        /* uppercase only from now on */
  782.  
  783.     while (t && *t) {        /* parse the flags */
  784.       if (*t == '\\') {        /* system flag? */
  785.     switch (*++t) {        /* dispatch based on first character */
  786.     case 'S':        /* possible \Seen flag */
  787.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  788.       t += 4;        /* skip past flag name */
  789.       break;
  790.     case 'D':        /* possible \Deleted flag */
  791.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  792.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  793.       t += 7;        /* skip past flag name */
  794.       break;
  795.     case 'F':        /* possible \Flagged flag */
  796.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  797.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  798.       t += 7;        /* skip past flag name */
  799.       break;
  800.     case 'A':        /* possible \Answered flag */
  801.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  802.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  803.       t += 8;        /* skip past flag name */
  804.       break;
  805.     default:        /* unknown */
  806.       i = 0;
  807.       break;
  808.     }
  809.                 /* add flag to flags list */
  810.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  811.       }
  812.       else {            /* no user flags yet */
  813.     t = strtok (t," ");    /* isolate flag name */
  814.     sprintf (err,"Unknown flag: %.80s",t);
  815.     t = strtok (NIL," ");    /* get next flag */
  816.     mm_log (err,ERROR);
  817.       }
  818.     }
  819.   }
  820.   return f;
  821. }
  822.  
  823. /* Search support routines
  824.  * Accepts: MAIL stream
  825.  *        message number
  826.  *        pointer to additional data
  827.  * Returns: T if search matches, else NIL
  828.  */
  829.  
  830.  
  831. char phile_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  832. {
  833.   return T;            /* ALL always succeeds */
  834. }
  835.  
  836.  
  837. char phile_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  838. {
  839.   return mail_elt (stream,msgno)->answered ? T : NIL;
  840. }
  841.  
  842.  
  843. char phile_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  844. {
  845.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  846. }
  847.  
  848.  
  849. char phile_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  850. {
  851.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  852. }
  853.  
  854.  
  855. char phile_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  856. {
  857.   return NIL;            /* keywords not supported yet */
  858. }
  859.  
  860.  
  861. char phile_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  862. {
  863.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  864.   return (elt->recent && !elt->seen) ? T : NIL;
  865. }
  866.  
  867. char phile_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  868. {
  869.   return mail_elt (stream,msgno)->recent ? NIL : T;
  870. }
  871.  
  872.  
  873. char phile_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  874. {
  875.   return mail_elt (stream,msgno)->recent ? T : NIL;
  876. }
  877.  
  878.  
  879. char phile_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  880. {
  881.   return mail_elt (stream,msgno)->seen ? T : NIL;
  882. }
  883.  
  884.  
  885. char phile_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  886. {
  887.   return mail_elt (stream,msgno)->answered ? NIL : T;
  888. }
  889.  
  890.  
  891. char phile_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  892. {
  893.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  894. }
  895.  
  896.  
  897. char phile_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  898. {
  899.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  900. }
  901.  
  902.  
  903. char phile_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  904. {
  905.   return T;            /* keywords not supported yet */
  906. }
  907.  
  908.  
  909. char phile_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  910. {
  911.   return mail_elt (stream,msgno)->seen ? NIL : T;
  912. }
  913.  
  914. char phile_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  915. {
  916.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  917.   return (char) ((long) ((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  918. }
  919.  
  920.  
  921. char phile_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  922. {
  923.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  924.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  925. }
  926.  
  927.  
  928. char phile_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  929. {
  930.                 /* everybody interprets "since" as .GE. */
  931.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  932.   return (char)((long) ((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  933. }
  934.  
  935.  
  936. char phile_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  937. {
  938.   return search (LOCAL->buf,mail_elt (stream,msgno)->rfc822_size,d,n);
  939. }
  940.  
  941.  
  942. char phile_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  943. {
  944.   char *s = phile_fetchstructure (stream,msgno,NIL)->subject;
  945.   return s ? search (s,strlen (s),d,n) : NIL;
  946. }
  947.  
  948.  
  949. char phile_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  950. {
  951.   return phile_search_body (stream,msgno,d,n);
  952. }
  953.  
  954. char phile_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  955. {
  956.   char tmp[MAILTMPLEN];
  957.   tmp[0] = '\0';        /* initially empty string */
  958.                 /* get text for address */
  959.   rfc822_write_address (tmp,phile_fetchstructure (stream,msgno,NIL)->bcc);
  960.   return search (tmp,strlen (tmp),d,n);
  961. }
  962.  
  963.  
  964. char phile_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  965. {
  966.   char tmp[MAILTMPLEN];
  967.   tmp[0] = '\0';        /* initially empty string */
  968.                 /* get text for address */
  969.   rfc822_write_address (tmp,phile_fetchstructure (stream,msgno,NIL)->cc);
  970.   return search (tmp,strlen (tmp),d,n);
  971. }
  972.  
  973.  
  974. char phile_search_from (MAILSTREAM *stream,long m,char *d,long n)
  975. {
  976.   char tmp[MAILTMPLEN];
  977.   tmp[0] = '\0';        /* initially empty string */
  978.                 /* get text for address */
  979.   rfc822_write_address (tmp,phile_fetchstructure (stream,m,NIL)->from);
  980.   return search (tmp,strlen (tmp),d,n);
  981. }
  982.  
  983.  
  984. char phile_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  985. {
  986.   char tmp[MAILTMPLEN];
  987.   tmp[0] = '\0';        /* initially empty string */
  988.                 /* get text for address */
  989.   rfc822_write_address (tmp,phile_fetchstructure (stream,msgno,NIL)->to);
  990.   return search (tmp,strlen (tmp),d,n);
  991. }
  992.  
  993. /* Search parsers */
  994.  
  995.  
  996. /* Parse a date
  997.  * Accepts: function to return
  998.  *        pointer to date integer to return
  999.  * Returns: function to return
  1000.  */
  1001.  
  1002. search_t phile_search_date (search_t f,long *n)
  1003. {
  1004.   long i;
  1005.   char *s;
  1006.   MESSAGECACHE elt;
  1007.                 /* parse the date and return fn if OK */
  1008.   return (phile_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1009.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1010. }
  1011.  
  1012. /* Parse a flag
  1013.  * Accepts: function to return
  1014.  *        pointer to string to return
  1015.  * Returns: function to return
  1016.  */
  1017.  
  1018. search_t phile_search_flag (search_t f,char **d)
  1019. {
  1020.                 /* get a keyword, return if OK */
  1021.   return (*d = strtok (NIL," ")) ? f : NIL;
  1022. }
  1023.  
  1024.  
  1025. /* Parse a string
  1026.  * Accepts: function to return
  1027.  *        pointer to string to return
  1028.  *        pointer to string length to return
  1029.  * Returns: function to return
  1030.  */
  1031.  
  1032.  
  1033. search_t phile_search_string (search_t f,char **d,long *n)
  1034. {
  1035.   char *end = " ";
  1036.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1037.   if (!c) return NIL;        /* missing argument */
  1038.   switch (*c) {            /* see what the argument is */
  1039.   case '{':            /* literal string */
  1040.     *n = strtol (c+1,d,10);    /* get its length */
  1041.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1042.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1043.       char e = *--c;
  1044.       *c = DELIM;        /* make sure not a space */
  1045.       strtok (c," ");        /* reset the strtok mechanism */
  1046.       *c = e;            /* put character back */
  1047.       break;
  1048.     }
  1049.   case '\0':            /* catch bogons */
  1050.   case ' ':
  1051.     return NIL;
  1052.   case '"':            /* quoted string */
  1053.     if (strchr (c+1,'"')) end = "\"";
  1054.     else return NIL;
  1055.   default:            /* atomic string */
  1056.     if (*d = strtok (c,end)) *n = strlen (*d);
  1057.     else return NIL;
  1058.     break;
  1059.   }
  1060.   return f;
  1061. }
  1062.